
local function errMsgFunc(errMsg)
    print("Error=" .. errMsg)
end

local function genNewCo(coRunFunc)
    local function coFunc(...)
        -- first run, ... get from first resume
        local retTbl = {pcall(coRunFunc, ...)}
        if not retTbl[1] then
            errMsgFunc(retTbl[2])
        end

        -- Recycling
        while true do
            -- yield return to co_resume, and expect new coRunFunc
            coRunFunc = coroutine.yield(unpack(retTbl))

            -- yield accept new actual parameters
            retTbl = {pcall(coRunFunc, coroutine.yield())}
            if not retTbl[1] then
                errMsgFunc(retTbl[2])
            end
        end
    end

    local co = coroutine.create(coFunc)
    return co
end

local coPool = setmetatable({}, { __mode = "kv" })

local function co_create(coRunFunc)
    local co = table.remove(coPool)
    if not co then
        print("--------- gen new one ---------")
        return genNewCo(coRunFunc)
    else
        print("--------- repeat use ---------")
        coroutine.resume(co, coRunFunc)
        return co
    end
end

local function co_resume(co, ...)
    -- run real func
    local retTbl = {coroutine.resume(co, ...)}
    -- reback to pool
    table.insert(coPool, co)
    -- retTbl[1] must be true, lose it
    return unpack(retTbl, 2)
end

------------------------------ test ------------------------------

local function test()
    print("test func running")
    assert(false)
    return 1, 2, 3
end

local co = co_create(test)
local a, b, c = co_resume(co)
print("1 run =", a, b, c)

co = co_create(test)
a, b, c = co_resume(co)
print("2 run =", a, b, c)

local function test2(num1, num2)
    local total = num1 + num2
    print("add result = " .. total)
    return total
end

co = co_create(test2)
a, b, c = co_resume(co, 11, 44)
print("3 run =", a, b, c)